// Extra drawing functions
//

#include "Common/Debug.h"
#include "Common/Macros.h"
#include "DrawShared.h"
#include "DrawNC.h"
#include "Draw.h"
#include "DrawEx.h"

void g_FillRectRaised(const TCanvas *canvas, int l, int t, int r, int b, int expand, int colors[])
{
	g_FillRect(canvas, l, t, r, b, colors[0]);

	if (expand < 0)
	{	
		expand = -expand;
		
		l += expand;
		t += expand;
		r -= expand;
		b -= expand;
	}

	while (expand--)
	{
		l--; t--; r++; b++;
		g_DrawHLine(canvas, l, t, r, colors[1]);
		g_DrawHLine(canvas, l, b, r, colors[2]);
		g_DrawVLine(canvas, l, t+1, b-1, colors[1]);
		g_DrawVLine(canvas, r, t+1, b-1, colors[2]);
	}
}

// NOTE: not support horizontal yet! 
// PLAN: add mid point color
void g_FillRectGradient(const TCanvas *canvas, int l, int t, int r, int b, int dir, int colors[])
{
    int width, height, i, length;
    UInt8 *ptr;
    int r1, g1, b1, r2, g2, b2, color;
	int lo, hi;

	// Clip left-right
    if (l < canvas->Left)  l = canvas->Left;
    if (r > canvas->Right) r = canvas->Right;

	// set top-down limit, so we can clip by checking bound in drawing loop
	lo = canvas->Top;
	hi = canvas->Bottom;

    i = 0;
    length = b - t + 1;

    ptr = FRAME_ANCHOR(l, t);

    width  = r - l + 1;
    height = b - t + 1;

    r1 = _GetRValue(colors[0]);
    g1 = _GetGValue(colors[0]);
    b1 = _GetBValue(colors[0]);

    r2 = _GetRValue(colors[1]);
    g2 = _GetGValue(colors[1]);
    b2 = _GetBValue(colors[1]);

    while (height--)
    {
		if (t > hi)
			break;
		if (t > lo)
		{
			color = _RGB(r1 + (i * ( r2 - r1 ) / length ),
				         g1 + (i * ( g2 - g1 ) / length ),
					     b1 + (i * ( b2 - b1 ) / length ));
#ifdef __RGB16
			color |= (color << 16);
#endif
			memset_color(ptr, color, width);
		}

		t++;
	    i++;
        ptr += FRAME_STRIDE;
    }
}

void g_DrawRectThick(TCanvas *canvas, int l, int t, int r, int b, int color, int thick, int stroke)
{
	int fix = 0;

	ASSERT_DEBUG(thick > 0);

	if (stroke == STROKE_CENTER)
	{
		fix = thick / 2;
	}
	else if (stroke == STROKE_OUTSIDE && thick > 1)
	{
		fix = thick;
	}

	l -= fix;
	t -= fix;
	r += fix;
	b += fix;	

	while (thick--)
	{
		l++; t++; r--; b--;
		g_DrawHLine(canvas, l, t, r, color);
		g_DrawHLine(canvas, l, b, r, color);
		g_DrawVLine(canvas, l, t + 1, b - 1, color);
		g_DrawVLine(canvas, r, t + 1, b - 1, color);
	}	
}


void g_DrawRectRound(TCanvas* pCanvas, int left, int top, int right, int bottom, int thick, int clr)
{
	int i, x1, y1, x2, y2;

	if( thick == 1 )
	{
		g_DrawVLine( pCanvas, left, top + 1, bottom - 1, clr );
		g_DrawVLine( pCanvas, right,top + 1, bottom - 1, clr );
		g_DrawHLine( pCanvas, left + 1, top, right - 1, clr );
		g_DrawHLine( pCanvas, left + 1, bottom, right - 1, clr );

		return;
	}

	//draw left/right line
	x1 = left + thick - 1;
	x2 = right - thick + 1;
	y1 = top;
	y2 = bottom;
	for( i = 0; i < thick; i++ )
	{
		g_DrawVLine( pCanvas, x1, y1, y2, clr );
		g_DrawVLine( pCanvas, x2, y1, y2, clr );
		x1--;
		x2++;
		y1++;
		y2--;
	}

	//draw top/bottom line
	x1 = left + thick;
	x2 = right - thick;
	y1 = top;
	y2 = bottom;
	for( i = 0; i < thick; i++ )
	{
		g_DrawHLine( pCanvas, x1, y1, x2, clr );
		g_DrawHLine( pCanvas, x1, y2, x2, clr );
		y1++;
		y2--;
	}
}

void g_DrawLineThick(TCanvas *canvas, int x1, int y1, int x2, int y2, int color, int thick)
{
	int dx = ABS_SUB(x1, x2);
	int dy = ABS_SUB(y1, y2);
	int t;
	
    // Draw a filled vertical line only
    if (!dx)
    {
        x1 -= (thick >> 1);
        x2 = x1 + thick -1;

        if (y1 > y2)
           SWAP(y1, y2);

		g_FillRect(canvas, x1, y1, x2, y2, color);

        return;
    }

    // Draw a filled horizontal line only
    if (!dy)
    {
        y1 -= (thick >> 1);
        y2 = y1 + thick -1;

        if (x1 > x2)
           SWAP(x1, x2);

		g_FillRect(canvas, x1, y1, x2, y2, color);

        return;
    }

    // draw sloping line only
    if ((dy / dx) == 1 || (dx / dy) == 1)
        thick++;

    t = thick;

    g_ModifyClip(canvas, 0, 0, -t, -t);

    if (dy > dx)
	{
        x1 -= (thick >> 1);
        x2 -= (thick >> 1);

        if (g_LineClipping(canvas, &x1, &y1, &x2, &y2))
        {
            while (thick--)
                g_DrawLineNC(canvas, x1++, y1, x2++, y2, color);
        }
    }
    else
    {
        y1 -= (thick >> 1);
        y2 -= (thick >> 1);

        if (g_LineClipping(canvas, &x1, &y1, &x2, &y2))
        {
            while (thick--)
                g_DrawLineNC(canvas, x1, y1++, x2, y2++, color);
        }
    }

    g_ModifyClip(canvas, 0, 0, t, t);
}


#define _MORE_BLUR	// enable more blurred effect in anti-aliased line

void g_DrawLineThickAA(TCanvas *canvas, int x1, int y1, int x2, int y2, int color, int thick)
{
	int dx = ABS_SUB(x1, x2);
	int dy = ABS_SUB(y1, y2);
	int t;
	int r = thick >> 1;

	if( x1 > canvas->Left+r && x1 < canvas->Right-r && y1 > canvas->Top+r && y1 < canvas->Bottom-r )
		g_FillCircle( canvas, x1, y1, r, color );
	if( x2 > canvas->Left+r && x2 < canvas->Right-r && y2 > canvas->Top+r && y2 < canvas->Bottom-r )
		g_FillCircle( canvas, x2, y2, r, color );

    // Draw a filled vertical line only
    if( dx == 0 )
    {
        x1 -= (thick >> 1);
        x2 = x1 + thick - 1;
		
        if (y1 > y2)
			SWAP(y1, y2);
		
		g_FillRect(canvas, x1, y1, x2, y2, color);

#ifdef _MORE_BLUR
		g_DrawVLineDarken(canvas, x1-1, y1, y2, color);
		g_DrawVLineDarken(canvas, x2+1, y1, y2, color);
#endif
        return;
    }
	
    // Draw a filled horizontal line only
    if( dy == 0 )
    {
        y1 -= (thick >> 1);
        y2 = y1 + thick - 1;
		
        if (x1 > x2)
			SWAP(x1, x2);
		
		g_FillRect(canvas, x1, y1, x2, y2, color);

#ifdef _MORE_BLUR
		g_DrawHLineDarken(canvas, x1, y1-1, x2, color);
		g_DrawHLineDarken(canvas, x1, y2+1, x2, color);
#endif		
        return;
    }

    // draw sloping line only
    if ((dy / dx) == 1 || (dx / dy) == 1)
        thick++;
	
    t = thick;

#ifdef _MORE_BLUR	
    g_ModifyClip(canvas, 1, 1, -t-1, -t-1);
#else
    g_ModifyClip(canvas, 0, 0, -t, -t);
#endif
	
    if (dy > dx)
	{
        x1 -= (thick >> 1);
        x2 -= (thick >> 1);
		
        if (g_LineClipping(canvas, &x1, &y1, &x2, &y2))
        {
            while (thick--)
                g_DrawLineNC(canvas, x1++, y1, x2++, y2, color);

#ifdef _MORE_BLUR
			g_SetLineAABitsLevel(8, 256);
#endif
			g_DrawLineAANC(canvas, x1-1, y1, x2-1, y2, color);
			g_DrawLineAANC(canvas, x1-t, y1, x2-t, y2, color);
#ifdef _MORE_BLUR
			g_SetLineAABitsLevel(7, 128);
			g_DrawLineAANC(canvas, x1, y1, x2, y2, color);
			g_DrawLineAANC(canvas, x1-t-1, y1, x2-t-1, y2, color);
#endif
        }
    }
    else
    {
        y1 -= (thick >> 1);
        y2 -= (thick >> 1);
		
        if (g_LineClipping(canvas, &x1, &y1, &x2, &y2))
        {
            while (thick--)
                g_DrawLineNC(canvas, x1, y1++, x2, y2++, color);

#ifdef _MORE_BLUR
			g_SetLineAABitsLevel(8, 256);
#endif
			g_DrawLineAANC(canvas, x1, y1-1, x2, y2-1, color);
			g_DrawLineAANC(canvas, x1, y1-t, x2, y2-t, color);
#ifdef _MORE_BLUR
			g_SetLineAABitsLevel(7, 128);
			g_DrawLineAANC(canvas, x1, y1, x2, y2, color);
			g_DrawLineAANC(canvas, x1, y1-t-1, x2, y2-t-1, color);
#endif
        }
    }

#ifdef _MORE_BLUR	
    g_ModifyClip(canvas, -1, -1, t+1, t+1);
#else
    g_ModifyClip(canvas, 0, 0, t, t);
#endif
}

void g_DrawPolylineAA(const TCanvas *canvas, const TPoint *points, int count, int color)
{
	int x1, y1, x2, y2;

	if (points[count - 1].x != points[0].x ||
        points[count - 1].y != points[0].y)
    {
		x1 = points[count - 1].x;
		y1 = points[count - 1].y;
		x2 = points[0].x;
		y2 = points[0].y;

		if (g_LineClipping(canvas, &x1, &y1, &x2, &y2))
			g_DrawLineAANC(canvas, x1, y1, x2, y2, color);
    }

    while (count > 1)
    {
		x1 = points[count - 1].x;
		y1 = points[count - 1].y;
		x2 = points[count - 2].x;
		y2 = points[count - 2].y;

		if (g_LineClipping(canvas, &x1, &y1, &x2, &y2))
			g_DrawLineAANC(canvas, x1, y1, x2, y2, color);
		
        count--;
    }
}

#define	WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4)

void g_DrawMonoImage(TCanvas *canvas, int x, int y, const TImage* image, int fg, int bg)
{
    int width, row, height;
	const UInt8 *data, *src;
	int clr;
	int w, count, b;	
	int _x;

	width = image->width;
	row	= WIDTHBYTES(width);
    height = image->height;
    data = (UInt8*)image->data;
	
	// Process row scan bits to color
  	while (height--)
	{
		src = data;
		_x = x;

		for (w = 0; w < width; w += 8)
		{
			if (width - w >= 8)
				count = 8;
			else
				count = width % 8;

			for (b = 7; b > 7 - count; b--)
			{
				clr = (*(src) & (1 << b)) ? bg : fg;
				g_DrawPixel(canvas, _x++, y, clr);			
			}
			src++;
		}
		
		data += row;
		y++;
	}
}
